package com.appistry.maven.plugin.far.remote.ssh;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;

/**
 * This class handles execution of FTP actions over a secure channel. It uses
 * the JSch library, specifically the {@link com.jcraft.jsch.ChannelSftp
 * ChannelSftp} class to invoke actions on a remote host
 * 
 * <p>
 * Not all FTP actions are supported currently. Actions supported so far:
 * <ul>
 * <li><code>put</code> file</li>
 * <li><code>get</code> file</li>
 * </ul>
 * <strong>Note:</strong> Additional commands will be supported as the need
 * arises.
 * </p>
 * 
 * <p>
 * 
 * @see com.jcraft.jsch.ChannelSftp
 * @see com.jcraft.jsch.Session
 * 
 */

public class SshFtp implements JSchWrapper {

	private SshConfig sshConfig;
	private Session session;
	private int exitStatus = -1;
	private String result;
	private String error;

	public SshConfig getSshConfig() {
		return sshConfig;
	}

	public void setSshConfig(SshConfig sshConfig) {
		this.sshConfig = sshConfig;
	}

	public int getExitStatus() {
		return exitStatus;
	}

	public void setExitStatus(int exitStatus) {
		this.exitStatus = exitStatus;
	}

	public String getResult() {
		return result;
	}

	public void setResult(String result) {
		this.result = result;
	}

	public String getError() {
		return error;
	}

	public void setError(String error) {
		this.error = error;
	}

	public SshFtp() {
		super();
	}

	public SshFtp(SshConfig sshConfig) {
		this.sshConfig = sshConfig;
	}

	/**
	 * This method opens a ssh session to the remote host, performs the FTP
	 * operation over a channel, and closes the channel and session when
	 * finished
	 * 
	 * @param src
	 *            source file
	 * @param dest
	 *            destination to put the file under
	 * @param type
	 *            file transfer operation type (put or get)
	 * @throws JSchException
	 * @throws SftpException
	 * 
	 * @see com.appistry.maven.plugin.far.remote.ssh.Sftp.TYPE
	 */
	public void executeCommand(String src, String dest, String type)
			throws JSchException, SftpException {
		System.out.println("SshFtp.executeCommand - START");

		try {

			// get a valid JSch Session
			openSshSession();

			// execute command
			if ("put".equalsIgnoreCase(type)) {
				putFile(src, dest);
			} else if ("get".equalsIgnoreCase(type)) {
				getFile(src, dest);
			}

		} finally {
			// cleanup
			closeSshSession();
		}
	}

	private void putFile(final String src, final String dest)
			throws JSchException, SftpException {
		Channel channel = null;

		try {
			channel = session.openChannel(CHANNEL_SFTP);
			channel.connect();

			((ChannelSftp) channel).put(src, dest);

			// setting the exit status code to 0 (SUCCESS)
			// TODO: Need to catch the exceptions and rethrow them, but at the
			// same time set the exit status code to, say -1 (FAILURE)
			setExitStatus(0);

		} finally {

			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
				channel = null;
			}
		}
	}

	private void getFile(final String src, final String dest)
			throws JSchException, SftpException {
		Channel channel = null;

		try {
			channel = session.openChannel(CHANNEL_SFTP);
			channel.connect();

			((ChannelSftp) channel).get(src, dest);

			// setting the exit status code to 0 (SUCCESS)
			// TODO: Need to catch the exceptions and rethrow them, but at the
			// same time set the exit status code to, say -1 (FAILURE)
			setExitStatus(0);

		} finally {

			if (channel != null && !channel.isClosed()) {
				channel.disconnect();
				channel = null;
			}
		}
	}

	private void openSshSession() throws JSchException {

		JSch jsch = new JSch();

		if (session == null) {
			session = jsch.getSession(sshConfig.getSshUserInfo().getUsername(),
					sshConfig.getHost(), sshConfig.getPort());
		}

		// assign UserInfo
		session.setUserInfo(sshConfig.getSshUserInfo());

		// session connect
		if (!session.isConnected()) {
			session.connect();
		}
		System.out.println("Session connected");

	}

	private void closeSshSession() {
		if (session != null && session.isConnected()) {
			session.disconnect();
		}
	}

}
